#include "BNBase.h"


// Constructor
// Param 1 = value as base 10
// Param 2 = the new base to use
BNBase::BNBase(char *_orig, char *_base, char *_length) {
	// initialize values
	this->head = this->tail = this->curr = NULL;

	// load up the new base number as a link list
	int index = 0;

	BIGNUM *orig = BN_new();
	BIGNUM *base = BN_new();
	BN_CTX *ctx = BN_CTX_new();

	BN_dec2bn(&orig, _orig);
	BN_dec2bn(&base, _base);

	BIGNUM *result = BN_new();
	BIGNUM *rem = BN_new();

	// while orig > base
	BN_div(result, rem, orig, base, ctx);

	// save off first base number
	char *tmp = BN_bn2dec(rem);
	this->append(tmp);
	OPENSSL_free(tmp);

	while(!BN_is_zero(result)) {
		// save off next base number
		BN_div(result, rem, result, base, ctx);

		char *tmp = BN_bn2dec(rem);
		this->append(tmp);
		OPENSSL_free(tmp);
	}

	// we want to prepend '0' if needed, to get the right length
	BIGNUM *count = BN_new();
	BN_dec2bn(&count, _length);
	curr = head;
	while(curr) {
		BN_sub_word(count, 1);
		curr = curr->next;
	}
	// append if needed
	while(!BN_is_zero(count)) {
		append("0");
		BN_sub_word(count, 1);
	}
	// clean up append variable
	BN_free(count);

	// clean up other resources
	BN_free(rem);
	BN_free(result);
	BN_CTX_free(ctx);
	BN_free(base);
	BN_free(orig);
}

// Deconstructor
BNBase::~BNBase() {
//	printf("BNBase::~BNBase()\n");
	curr = head;
	while(curr) {
		_bnbase *tmp = curr;
		curr = curr->next;

		// Free up the BIGNUM*
		BN_free(tmp->value);
		// Free up the struct
		delete(tmp);
	}
}


// form the number with the new base
void BNBase::append(char *next) {
//	printf("BNBase::append(%s)\n", next);
	// form a new link list node
	_bnbase *number = new _bnbase;
	number->next = number->prev = NULL;
	number->value = BN_new();
	BN_dec2bn(&(number->value), next);

	// attach to link list
	if(head == NULL) {
		// first node
		head = tail = number;
	}
	else {
		// attach to tail
		tail->next = number;
		number->prev = tail;
		tail = number;
	}
}

// reset the curr pointer in the link list to the head node and return the head value
BIGNUM* BNBase::reset() {
//	printf("BNBase::reset()\n");
	curr = head;

	if(curr) {
		return curr->value;
	}

	return NULL;
}

// move the curr pointer forward in the link list and return the next node value
BIGNUM* BNBase::getNext() {
//	printf("BNBase::getNext()\n");
	if(curr) {
		curr = curr->next;
		if(curr) {
			return curr->value;
		}
	}

	return NULL;
}